// PageManager.h

#ifndef _PAGEMANAGER_H_
#define _PAGEMANAGER_H_

#include "../include/TypeStat.h"
#include <stdint.h>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>


/*
存储管理
*/

namespace MManage{

    /* 存储管理核心类前调声明 */
    class FileController;
    class GroupInfo;
    class PageInfo;

    /* 页面/组容量参数 注意参数间相互的制约关系 */

    //单个页面的大小(单位:字节)
    const int PageSize_Numofubeta8 = 16384;

    //单个页面内组的个数
    const int Group_NumInPage = 58;

    //单个组的大小(单位:字节)
    const int GroupSize = 280;

    //单个页面前缀区大小(单位:字节)
    const int PrefixSize = 72;

    //单个页面后缀区大小(单位:字节)
    const int SuffixSize = 72;

    //页面池最大容量
    const int MaxPages = 10;

    //单个组内数据的最大容量(单位:字节)
    const int MaxDataContext_perGroup = 276;

    
    /*
    底层文件操作封装
    负责页面在文件中的大规模操作
    */
    class FileController{
    public:
        int __file_fd;//文件描述符
        int pagenum;//现存页面个数
        char name[100];//表名称
        __off_t __realsize;//文件内容全体大小(包括空洞在内)

    public:
        FileController(const char* tbname);

    public:
        // 打开表文件(内部函数)
        // 成功:0 并设置类内文件描述符
        // 失败:-1 并设置类内文件描述符为-1
        int __open();

        // PageIndex => offset
        __off_t __get_offset(int pageIndex);

        // offset => PageIndex
        int __get_PageIndex(__off_t offSet);

        // 写函数:覆盖写一个页面 页面信息在pointer指向的页面空间中
        // 成功:0 失败:-1
        int writePage(const ubeta8 pageIndex,const ubeta8* pointer,int wtsize);

        // 读函数:根据pageIndex指定的页面号将数据写入指定的data内存中
        // 成功:0 读取内容不会超过一个页面大小
        // 失败:-1
        int readPage(const ubeta8 pageIndex,ubeta8* data,int rdsize=PageSize_Numofubeta8);

        //检查对应页面号的文件空间是否存在
        int __exist_Page(const ubeta8 pageIndex);

        int __init_PageSpace(int pageIndex);

        //在文件中向后添加一个新的页面并初始化基本信息
        int __alloc_Back();

        // 删除文件中的最后一个页面的空间
        // 成功:0
        // 失败:-1
        int __delet_Back();

        //将某块页面的内容拷贝到其他页面上
        //该函数直接覆盖内容 不做页面数据逻辑有效性检测
        int __copy_Space(int from_pageIndex,int to_pageIndex);

        //关闭文件描述符
        void __close();

        //文件内容同步到磁盘
        int Self_Sync();
        
    };


    /*
    [组对象]只是保存页面中的组在内存中的位置
    它同自己相应的[页面对象]共生死
    它只提供解析功能和修改功能
    */
    class GroupInfo{
    //SUM = 280B
    //分配标志位(1) + 组号(6) + 后置组编号(6) + 前置组编号(6) + 空白(4) + 数据段总长(9) + 存储段(276B)
    private:
        ubeta8* inner_pointer = nullptr;
    public:
        GroupInfo(ubeta8* grpos = nullptr);
        ~GroupInfo();
    public:
        const static ubeta8 FULL_UBETA8 = 0xFF;
        const static ubeta16 FULL_UBETA16 = 0xFFFF;
    public://操作集合

        //是否分配
        bool isDistri();
		void setDistri(bool stat = false);

        //组编号-ubeta8低6位有效
        ubeta8 getGroupIndex();
        void setGroupIndex(ubeta8 index);

        //后一组编号-ubeta8低6位有效
        ubeta8 getNextIndex();
        void setNextIndex(ubeta8 index);

        //前一组编号-ubeta8低6位有效
        ubeta8 getLastIndex();
        void setLastIndex(ubeta8 index);

		//空白数据-ubeta8低4位有效
        ubeta8 getSpInfo();
        void setSpInfo(ubeta8 info);

        //数据区总长度-ubeta16低9位有效
        ubeta16 getDataLenth();
        void setDataLenth(ubeta16 lenth);

        //分配标志位(1) + 组号(6) + 后置组编号(6) + 前置组编号(6) + 空白(4) + 数据段总长(9) + 存储段(276B)
        // 写数据
        // 返回实际写入的数据长度
        ubeta16 writeData(const ubeta8* data,int wtsize);

        // 读取数据
        // 返回实际读取的数据长度
        ubeta16 readData(ubeta8* data);

    };

    /*
    空间的申请权限在PageInfo类中
    只有该类能够调用malloc()函数
    PageIndex在文件内是唯一的
    */
    class PageInfo{
    public:
        ubeta8 PageIndex;//页面编号
        ubeta8* mm_space = nullptr;//指向申请到的空间
        ubeta8* mm_grp = nullptr;//首个组起始地址
        ubeta8* mm_backspace = nullptr;//后标注区
        //静态缓冲空间-空间指针:用于解决数据读取时的长度不稳定因素
        ubeta8* data_pointer = nullptr;
        //FileController指针
        FileController* table_fcntler = nullptr;
        //页面内各个分组的位置
        ubeta8* grplis[Group_NumInPage];
        //各个分组的标记状态
        int vis[Group_NumInPage];
        //空闲分组统计
        int space_grp_cnt = 0;
        //周期性修改标记
        int modify_cnt = 0;
        //同步周期
        int sync_round = 12;

    public:
        //页面创建 自动分配内存空间 能够指定是否连接到文件
        PageInfo(const ubeta8 PageIndex,FileController* tbfer=nullptr);

        //资源释放
        int __release();

        //周期性同步到文件实体
        void round_Sync();

        //同步到文件实体
        void Self_Sync();

        ~PageInfo();
    public:
        /*数据组的读写规则
        1]无效组标记为有效组后，组内存的所有位置都应当被重新设置。即便没有必要，也需要清空原有的数据。
        2]有效组被删除只需要改变其组标记
        3]删除一条记录实际操作是删除与指定组标记前后相关联的页面内所有组，即组链无效化
        4]组如果没有前置或后置组，应当将标记的所有bit全部置为1
        5]PageInfo类应当能够有对应的状态记录数组标记出当前页面内的填充状态
        6]PageInfo类应当周期性的同步内存数据到文件
        7]PageInfo对象是否存在对应的FileController应当在构造期间指出
        */

        /* 单个组的分配与回收 */

        // [1]使得组有效-将标记区清空-对象必须是无效组
        // 错误:指定的组已经被有效化 -1
        // 错误:指定的组不存在 -2
        // 成功:0
        int __DistriGroup(const ubeta8 groupIndex);

        // [2]删除单个有效组
        // 错误:指定的组已经被有效化 -1
        // 错误:指定的组不存在 -2
        // 成功:0
        int __inDistriGroup(const ubeta8 groupIndex);

        /* 完整组链的查找 */

        // 根据单个结点获取整条组链
        // 失败: -1 指定的组编号无效
        // 失败: -2 指定的组无效
        // 成功: 0
        int __getGroupList(const ubeta8 nodeIndex,GrpList& result_lis);
        
        /* 组链的创建与删除 */

        // 设置已分配的组链的标记以使其串联-调用前应先使组有效化
        int __initGroupList(const GrpList lis);

        // 分配指定个数的组并形成组链
        // 失败:-1 没有足够的页面空间可供分配
        // 成功:0
        int __alloc_GrpList(int alloc_num,GrpList& result_lis);

        // [3]组链删除
        // 失败: -1 组链获取失败
        // 失败: -2 循环删除失败
        // 成功: 0
        int __deleteGroupList(const ubeta8 nodeIndex);

        /* 组链的读写 */

        // 直接向组链中写入数据
        void __direct_write(GrpList lis,const ubeta8* data,int wtsize);

        // 写特定的组涉及到的组链
        // 失败: -1 没有找到指定组所在的组链
        // 失败: -2 组链延长操作失败
        // 失败: 1  空间不足需要请求其他页面(不会删除现有的存储空间)
        // 成功: 0
        int Write(const ubeta8 GroupIndex,const ubeta8* data,int wtsize);

        // 写数据到页面
        // 失败: -1 空闲组不满足数据存储大小
        // 失败: -2 分配组链失败
        // 成功: 0 返回分配的索引Index
        int Write(const ubeta8* data,int wtsize,Index& result_index);

        /* 数据读取 */
        // 数据读取准备-该函数可连续调用 但 最后必须调用Read_Over()回收内存
        // 成功:返回准备好的数据长度-数据准备在了对象的this->data_pointer指向的空间中
        // 失败: -1 没有找到组链
        int Read_Ready(const ubeta8 groupIndex);
        //数据读取完毕后用于回收对象所有的数据缓冲区
        void Read_Over();
    };
}



#endif // _PAGEMANAGER_H_

